AVL树是最先发明的自平衡
二叉查找树。在AVL树中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度
平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次
树旋转来重新平衡这个树。
AVL的含义
1.品质管理系统中,AVL是Approved Vendor List,及一般意义上的合格供应商目录。
2.在计算机科学中,AVL树是最先发明的自平衡
二叉查找树AVL 节点数计算
高度为 h 的 AVL 树, 节点数 N 最多2 − 1; 最少 ( 其中 )。
最少节点数 n 如以费伯纳西数列可以用
数学归纳法证明:
Nh = Fh + 2 - 1 (Fh + 2是 Fibonacci polynomial)。
即:
N0 = 0 (表示 AVL Tree 高度为0的节点总数)
N1 = 1 (表示 AVL Tree 高度为1的节点总数)
N2 = 2 (表示 AVL Tree 高度为2的节点总数)
Nh = Nh − 1 + Nh − 2 + 1 (表示 AVL Tree 高度为h的节点总数)
换句话说,当节点数为 N 时,高度 h 最多为 。
节点的平衡因子是它的右子树的高度减去它的左子树的高度。带有平衡因子 1、0 或 -1 的节点被认为是平衡的。带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个
节点中,或从可能存储在节点中的子树高度计算出来。
AVL 操作
AVL树的基本操作一般涉及运做同在不平衡的
二叉查找树假设由于在
二叉排序树上插入结点而失去平衡的最小子树根结点的
指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行进行的规律可归纳为下列四种情况:
单向右旋平衡处理RR:由于在*a的左子树根结点的左子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次右旋转操作;
单向左旋平衡处理LL:由于在*a的右子树根结点的右子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次左旋转操作;
双向旋转(先左后右)平衡处理LR:由于在*a的左子树根结点的右子树上插入结点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。
双向旋转(先右后左)平衡处理RL:由于在*a的右子树根结点的左子树上插入结点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。
插入
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根
节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根
节点的路途上最多有 1.5 乘 log n 个节点,而每次 AVL 旋转都耗费恒定的时间,插入处理在整体上耗费 O(log n) 时间。
在平衡的的
二叉排序树Balanced BST上插入一个新的
数据元素e的
递归算法可描述如下:
若BBST为空树,则插入一个
数据元素为e的新结点作为BBST的根结点,树的深度增1;
若e的
关键字和BBST的根结点的关键字相等,则不进行;
若e的
关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:
BBST的根结点的平衡因子为-1(右子树的深度大于左子树的深度,则将根结点的平衡因子更改为0,BBST的深度不变;
BBST的根结点的平衡因子为0(左、右子树的深度相等):则将根结点的平衡因子更改为1,BBST的深度增1;
BBST的根结点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根结点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根结点和其右子树根结点的平衡因子更改为0,树的深度不变;
若e的
关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e有相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。
删除
从AVL树中删除可以通过把要删除的
节点向下旋转成一个
叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成
叶子节点期间最多有 log n个节点被旋转,而每次 AVL 旋转耗费恒定的时间,删除处理在整体上耗费 O(log n) 时间。
查找
在AVL树中查找同在一般BST完全一样的进行,所以耗费 O(log n) 时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查询而改变。(这是与
伸展树查找相对立的,它会因为查找而变更树结构。)
参考实现
给出一个操作AVLTREE的完整程序 大部分由Peter Brass编写
#include
#include
#include
//建立一个整数类型
typedef struct obj_n_t
{
int obj_key;
} obj_node_t;
//建立树结点的基本机构
typedef struct tree_n_t
{
int key;
struct tree_n_t *left,*right;
int height;
} tree_node_t;
//建立堆栈
#define MAXSIZE 512
tree_node_t *stack[MAXSIZE]; //warning!the tree can contain 256 leaves at most!
//堆栈清空
void stack_clear()
{
while(i!=0)
{
stack[i-1]=NULL;
i--;
}
}
//堆栈为空
int stack_empty()
{
return(i==0);
}
//入栈函数
int push(tree_node_t *node)
{
if(i
{
stack[i++]=node;
return(0);
}
else
return(-1);
}
//出栈函数
tree_node_t *pop()
{
if(i>0)
return(stack[--i]);
else
return(0);
}
//建立get_node函数,用于
动态分配内存空间
tree_node_t *get_node()
{
tree_node_t *tmp;
tmp=(tree_node_t *)malloc(sizeof(tree_node_t));
return(tmp);
}
//建立return_node函数,用于释放内存
void return_node(tree_node_t *free_node)